package nachos.threads;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import nachos.ag.BoatGrader;
import nachos.machine.*;


public class Boat
{
    static BoatGrader bg;
    static int oahuChildren = 0;
    static int oahuAdults = 0;
    static int peopleLeftBehind = 0;
    static int molokaiChildren = 0;
    static int boatCapacity = 2;
    static Lock boatLock = new Lock();
    static Condition2 oahu = new Condition2(boatLock);
    static Condition2 molokai = new Condition2(boatLock);
    static Condition2 childRideWait = new Condition2(boatLock);
    static boolean boatInOahu = true;

    public static void selfTest()
    {
	BoatGrader b = new BoatGrader();
	
	System.out.println("\n ***Testing Boats ***");
	begin(2, 6, b);

    }

    public static void begin( int adults, int children, BoatGrader b )
    {
	// Store the externally generated autograder in a class
	// variable to be accessible by children.
	bg = b;
        oahuChildren = children;
        oahuAdults = adults;
        peopleLeftBehind = children + adults;
	// Instantiate global variables here
	boolean state = Machine.interrupt().disable();
        ThreadedKernel.scheduler.setPriority(2); 
        Machine.interrupt().restore(state);
	// Create threads here. See section 3.4 of the Nachos for Java
	// Walkthrough linked from the projects page.
        LinkedList<KThread> joined = new LinkedList<KThread>();

        for(int i = 0; i < adults; i++){
            Runnable r = new Runnable() {
                public void run() {
                    AdultItinerary();
                }
            };
            KThread t = new KThread(r);
            t.setName("Adult " + i);
            t.fork();
            joined.add(t);
        }
        KThread next;

        for(int i = 0; i < children; i++){
            Runnable r = new Runnable() {
                public void run() {
                    ChildItinerary();
                }
            };
            KThread t = new KThread(r);
            t.setName("Child " + i);
            t.fork();
            joined.add(t);
        }

        try{
            while(( next = joined.removeLast()) != null)
                next.join();
        }catch(NoSuchElementException e){
            //when the list is empty
        }
        System.out.println("*****Ok all the people are in molokai");
    }

    static void AdultItinerary()
    {
        boatLock.acquire();
        // if the boat isn't in Oahu the adult will wait
        while(!boatInOahu || oahuChildren > 1 || boatCapacity < 2){
            oahu.sleep();
        }
        boatCapacity-= 2;
        //only one adult per boat, and only the adult, not the child
        bg.AdultRowToMolokai();
        boatInOahu = false;
        //one person less
        peopleLeftBehind--;
        oahuAdults--;
        boatCapacity+=2;
        //wake one person waiting in molokai (in this case a child)
        molokai.wake();
        boatLock.release();
    }

    static void ChildItinerary()
    {
        boolean imInOahu = true;
        boatLock.acquire();
        //only if there's people in oahu
        while(peopleLeftBehind > 0){
           if(imInOahu){
               /*if the boat isn't in oahu or the boat is at it's maximum
                *capacity or there if he is the only child and there's more
                *adults in the island.
                */
               while(!boatInOahu || boatCapacity < 1 || (oahuChildren < 2 && oahuAdults > 0)){
                   oahu.sleep();
               }              
               boatCapacity--;
               if(boatCapacity == 0){
               /*at this point there is two children in the boat
                *and both of the children are ready to go to molokai
                */
                   bg.ChildRideToMolokai();
                   peopleLeftBehind -=2;
                   oahuChildren -= 2;
                   molokaiChildren += 2;
                   imInOahu = false;
                   boatInOahu = false;
               //wake the child waiting to row to molokai with me
                   childRideWait.wake();                   
               }else{
               //at this point I (the child) will row to molokai.
                   bg.ChildRowToMolokai();
                   if(oahuChildren > 1){
                   /*at this point I know I must wait for another child,
                    *(the another child will ride the boat.
                    */
                       oahu.wakeAll();
                       imInOahu = false;
                       childRideWait.sleep();
                   }
                   else{
                   /*at this point I know im the only person on Oahu, so I will
                    * go to molokai
                    */
                       peopleLeftBehind--;
                       oahuChildren--;
                       molokaiChildren++;
                       imInOahu = false;
                       boatInOahu = false;
                   }
               }
               boatCapacity++;
               molokai.wake();

           }else {
              /*if im in molokai i will return because there still
               *people in oahu and i will wait while some one is using the boat
               *or the boat isn't in molokai
               */
              while(boatInOahu || boatCapacity < 2){
                   molokai.sleep();
              }
              
              boatCapacity--;
              bg.ChildRowToOahu();
              oahuChildren++;
              peopleLeftBehind++;
              imInOahu = true;
              boatInOahu = true;
              boatCapacity++;
              oahu.wakeAll();
           }           
        }
        boatLock.release();
    }
}
